
using ICSharpCode.SharpZipLib.Zip;

namespace Nutils
{
    public class Bundle
    {
        public Bundle()
        {
            _files = new System.Collections.Generic.Dictionary<string, byte[]>();
            _version = _head_version;
        }

        public void Add(string id, System.IO.Stream stream)
        {
            byte[] buffer = new byte[stream.Length];
            stream.Read(buffer, 0,(int)stream.Length);
            _files[id] = buffer;
        }

        public void AddString(string id, string s)
        {
            byte[] buffer = System.Text.Encoding.Unicode.GetBytes(s.ToCharArray());
            _files[id] = buffer;
        }

        // more efficent when going straight to the disk than 'Get'.
        public void Inflate(string id, System.IO.BinaryWriter inStream)
        {
            byte[] buffer = _files[id];
            inStream.Write(buffer, 0, buffer.Length);
        }

        public System.IO.StringReader GetString(string id)
        {
            System.Text.Encoding encode = System.Text.Encoding.Unicode;
            
            char[] buffer = new char[encode.GetCharCount(_files[id], 0, _files[id].Length)];
            encode.GetDecoder().GetChars(_files[id], 0, _files[id].Length, buffer, 0, false);

            return new System.IO.StringReader(new string(buffer));
        }

        // packs contents of current bundle to disk at given path.
        public void Pack(string path)
        {
            using (ZipOutputStream zipOutStream = new ZipOutputStream(System.IO.File.Create(path)))
            {
                zipOutStream.SetLevel(9);

                // version stamp
                ZipEntry versionEntry = new ZipEntry(_version_key);
                versionEntry.DateTime = System.DateTime.Now;

                zipOutStream.PutNextEntry(versionEntry);
                zipOutStream.Write(new byte[] { _version }, 0, 1);

                // everything else.
                foreach (string key in _files.Keys)
                {
                    ZipEntry entry = new ZipEntry(key);
                    entry.DateTime = System.DateTime.Now;

                    zipOutStream.PutNextEntry(entry);
                    zipOutStream.Write(_files[key], 0, _files[key].Length);
                }
            }
        }

        public void Unpack(string path)
        {
            using (System.IO.FileStream fileStream = System.IO.File.OpenRead(path))
            {
                using (ZipInputStream zip = new ZipInputStream(fileStream))
                {
                    ZipEntry entry;
                    while ((entry = zip.GetNextEntry()) != null)
                    {
                        byte[] data = new byte[entry.Size];
                        zip.Read(data, 0, (int)entry.Size);

                        if (entry.Name == _version_key)
                        {
                            if (entry.Size > 1)
                            {
                                Nutils.Console.Error("Size of version data is too large. Should only be one byte.");
                            }

                            _version = data[0];
                            continue;
                        }
                        
                        _files[entry.Name] = data;
                    }
                }
            }
        }

        public string[] Files
        {
            get
            {
                System.Collections.Generic.List<string> l = new System.Collections.Generic.List<string>();
                foreach (string s in _files.Keys)
                {
                    l.Add(s);
                }

                return l.ToArray();
            }
        }

        public byte Version
        {
            get
            {
                return _version;
            }
        }

        /// <summary>
        /// Internal data
        /// </summary>
        private System.Collections.Generic.Dictionary<string, byte[]> _files;
        private const byte _head_version = 1;
        private const string _version_key = "|version|";
        private byte _version;
    }

}